﻿using Emgu.CV;
using Emgu.CV.Util;
using ContourSplit.Scaffold;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace ContourSplit.Forms
{
	public partial class Main : Form
	{
		private MindVisionCamera camera;
		private PCIBoard pci;
		public Main()
		{
			InitializeComponent();
			try
			{
				camera = new MindVisionCamera(0);
				camera.OnReiceivingImage += Camera_OnReiceivingImage;
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
			}
			pci = new PCIBoard();
		}
		/// <summary>
		/// 返回矩形指定边的中点
		/// </summary>
		/// <param name="rect">矩形</param>
		/// <param name="index">-1：上；1：下；-2：左；2：右</param>
		/// <returns></returns>
		private Point getEdgeMiddle(Rectangle rect, int index)
		{
			switch (index)
			{
				case -1: return new Point(rect.Left + rect.Width / 2, rect.Top);
				case 1: return new Point(rect.Left + rect.Width / 2, rect.Bottom);
				case 2: return new Point(rect.Right, rect.Top + rect.Height / 2);
				case -2: return new Point(rect.Left, rect.Top + rect.Height / 2);
			}
			return new Point();
		}
		private void PutInfo(ListBox lbResult, List<Rectangle> rects, bool byVertical, double area)
		{
			lbResult.Items.Add($"<{(byVertical ? "纵向" : "横向")}> 轮廓面积:{area}, 找到{rects.Count}个矩形:");
			foreach (var item in rects)
			{
				if (byVertical)
					lbResult.Items.Add($"X: {item.X}; Y: [{item.Y},{item.Bottom}]");
				else
					lbResult.Items.Add($"X: [{item.X},{item.Right}]; Y: {item.Y}");
			}
			lbResult.Items.Add("-----------------");
			lbResult.SelectedIndex = lbResult.Items.Count - 1;
		}
		private void Camera_OnReiceivingImage(object sender, ReceivingArgs e)
		{
			Mat ori = new Mat(e.size, Emgu.CV.CvEnum.DepthType.Cv8U, e.channels, e.FrameBuffer, e.size.Width * e.channels);
			Console.WriteLine($"Received:{ori.Cols}*{ori.Rows}*{ori.NumberOfChannels}");
			Mat m = new Mat();
			Config.config.calibrator.Undistort(ori, m);
			ImageProcessor p = new ImageProcessor()
			{
				Threshold = Config.config.Threshold,
				WidthInPX = Config.config.WidthInPX,
			};
			p.ExtractMaxContour(m, out VectorOfPoint maxContour);

			Mat result = p.SplitContour(m, maxContour, out List<Rectangle> rects);
			imageBox1.Invoke(new Action<Mat>(delegate (Mat img) { imageBox1.Image = img; }), new object[] { result });
			Console.WriteLine("开始涂胶水...");
			PCIRectAction(-1, true, rects, Config.config.Offset);

			for (int i = 0; i < Config.config.Passes; i++)
			{
				result = p.SplitContour(m, maxContour, out rects, false);
				double area = CvInvoke.ContourArea(maxContour);
				imageBox1.Invoke(new Action<Mat>(delegate (Mat img) { imageBox1.Image = img; }), new object[] { result });
				if (i == 0)
					lbResult.Invoke(new Action<ListBox, List<Rectangle>, bool, double>(PutInfo), new object[] { lbResult, rects, false, area });
				Console.WriteLine($"横向第{i}遍涂抹...");
				PCIRectAction(-2, false, rects, Config.config.Offset);

				pci.MoveZ(Config.config.ZInitH);

				result = p.SplitContour(m, maxContour, out rects);
				if (i == 0)
					lbResult.Invoke(new Action<ListBox, List<Rectangle>, bool, double>(PutInfo), new object[] { lbResult, rects, true, area });
				imageBox1.Invoke(new Action<Mat>(delegate (Mat img) { imageBox1.Image = img; }), new object[] { result });
				Console.WriteLine($"纵向第{i}遍涂抹...");
				PCIRectAction(-1, false, rects, Config.config.Offset);

				pci.MoveZ(Config.config.ZInitH);

				if (!Config.config.PaintOnFinally)
				{
					Console.WriteLine("第{i}遍涂抹边缘...");
					PCIContourAction(maxContour, Config.config.Offset);
					if (i != Config.config.Passes - 1)
						pci.MoveZ(Config.config.ZInitH);
				}
			}
			if (Config.config.PaintOnFinally)
			{
				Console.WriteLine("开始涂抹边缘...");
				PCIContourAction(maxContour, Config.config.Offset);
			}
			pci.MoveHome();

			new Thread(() =>
			{
				pci.Output(Config.config.CompletePort, true);
				Thread.Sleep(10000);
				pci.Output(Config.config.CompletePort, false);
			}).Start();
		}
		void Pause()
		{
			Console.WriteLine("已暂停，按任意键继续...");
			Console.ReadKey();
		}
		private void PCIContourAction(VectorOfPoint maxContour, Point offset)
		{
			for (int i = 0; i < maxContour.Size; i++)
			{
				if (i == 0) pci.MoveZ(Config.config.ZActH);
				var x = maxContour[i]; x.Offset(offset);
				pci.MoveXY(x);
				//Pause();
			}
		}
		private void PCIRectAction(int direction, bool isDropGlue, List<Rectangle> rects, Point offset)
		{
			var zH = isDropGlue ? Config.config.ZInitH : Config.config.ZActH;
			ushort port = Config.config.GluePort;

			Point x = getEdgeMiddle(rects[0], direction); x.Offset(offset); pci.MoveXY(x); //Pause();
			pci.MoveZ(zH);
			if (isDropGlue) pci.Output(port, true);
			x = getEdgeMiddle(rects[0], -direction); x.Offset(offset); pci.MoveXY(x); //Pause();
			if (isDropGlue) pci.Output(port, false);
			for (int j = 1; j < rects.Count; j++)
			{
				x = getEdgeMiddle(rects[j], -direction); x.Offset(offset);
				pci.MoveXY(x); //Pause();
				if (isDropGlue) pci.Output(port, true);

				x = getEdgeMiddle(rects[j], direction); x.Offset(offset);
				pci.MoveXY(x); //Pause();
				if (isDropGlue) pci.Output(port, false);
				direction *= -1;
			}
		}

		private void btnStart_Click(object sender, EventArgs e)
		{
			if (camera != null)
				camera.Play();
		}

		private void btnStop_Click(object sender, EventArgs e)
		{
			if (camera != null)
				camera.Stop();
		}

		private void btnSetting_Click(object sender, EventArgs e)
		{
			Config old = new Config(Config.config);
			DialogResult result = new Setting().ShowDialog();
			if (result != DialogResult.OK)
			{
				Config.config = new Config(old);
			}
		}
		static bool t1 = true;
		private void btnOut_Click(object sender, EventArgs e)
		{
			pci.Output(Config.config.GluePort, t1);
			t1 = !t1;
		}

		private void btnMM_Click(object sender, EventArgs e)
		{
			var v = textBox1.Text;
			new Thread(() =>
			{
				pci.TurnTo(double.Parse(v));
			}).Start();
		}

		private void btnHome_Click(object sender, EventArgs e)
		{
			Console.WriteLine($"MoveHome Result:{pci.MoveHome()}");
		}

		private void button1_Click(object sender, EventArgs e)
		{
			CSPCI1000.PCIInvoke.w1000_close();
		}

		private void Main_FormClosing(object sender, FormClosingEventArgs e)
		{
			CSPCI1000.PCIInvoke.w1000_close();
		}

		bool t2 = true;
		private void button2_Click(object sender, EventArgs e)
		{
			pci.Output(Config.config.CompletePort, t1);
			t2 = !t2;
		}

	}
}
